package com.zrwl.poscloud.core.restapi.authrozation;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.zrwl.poscloud.base.entity.*;
import com.zrwl.poscloud.base.enums.EStatus;
import com.zrwl.poscloud.commons.config.jwt.Audience;
import com.zrwl.poscloud.commons.config.jwt.JwtTokenUtil;
import com.zrwl.poscloud.commons.feign.ProxyFeignClient;
import com.zrwl.poscloud.commons.utils.*;
import com.zrwl.poscloud.xo.global.MessageConf;
import com.zrwl.poscloud.xo.global.RedisConf;
import com.zrwl.poscloud.xo.global.SysConf;
import com.zrwl.poscloud.xo.service.*;
import com.zrwl.poscloud.xo.vo.UserAdminRVO;
import com.zrwl.poscloud.xo.vo.UserEmployeeRVO;
import com.zrwl.poscloud.xo.vo.UserRoleRVO;
import com.zrwl.poscloud.xo.vo.UserVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author 刘思童
 * @create 2021-07-05 15:18
 **/
@RestController
@RequestMapping("/customerLogin")
@Api(value = "手机端用户登录相关接口", tags = {"手机端用户登录相关接口"})
@Slf4j
public class CustomerLoginRestApi {

    @Autowired
    private UserRoleRService userRoleRService;
    @Autowired
    private AdminService adminService;
    @Autowired
    private RoleService roleService;
    @Autowired
    private UserService userService;
    @Autowired
    private UserAdminRService userAdminRService;
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    @Autowired
    private UserEmployeeRService userEmployeeRService;
    @Autowired
    private Audience audience;
    @Value(value = "${isRememberMeExpiresSecond}")
    private int isRememberMeExpiresSecond;
    @Autowired
    private RedisUtil redisUtil;
    @Resource
    private ProxyFeignClient proxyFeignClient;
    @Value(value = "${tokenHead}")
    private String tokenHead;

    @Value(value = "${tokenHeader}")
    private String tokenHeader;

    @ApiOperation(value = "获取图片验证码", notes = "获取图片验证码", response = String.class)
    @GetMapping(value = "/getImageValidCode")
    public String getImageValidCode() throws IOException {
        /*
             1.生成验证码
             2.把验证码上的文本存在session中
             3.把验证码图片发送给客户端
             */
        ImageVerificationCode ivc = new ImageVerificationCode();     //用我们的验证码类，生成验证码类对象
        BufferedImage image = ivc.getImage();  //获取验证码
        //通过随机数字生成sessionId;
        String wxSessionId = StringUtils.getUUID();
        JSONObject wxSession = new JSONObject();
        redisUtil.setEx(RedisConf.IMG_VALID_ID + RedisConf.SEGMENTATION + tokenHead + wxSessionId, ivc.getText(), 5, TimeUnit.MINUTES);
        Map result = new HashMap();
        result.put("validId", wxSessionId);
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        ImageIO.write(image, "png", stream);
        String base64 = Base64.getEncoder().encodeToString(stream.toByteArray());
        result.put("img", base64);
        return ResultUtil.successWithData(result);
//        ivc.output(image, response.getOutputStream());//将验证码图片响应给客户端
    }

    @ApiOperation(value = "验证图片验证码", notes = "验证图片验证码", response = String.class)
    @GetMapping(value = "/validImageValidCode")
    public boolean validImageValidCode(@RequestParam(name = "validId", required = true) String validId, @RequestParam(name = "imageValidCode", required = true) String imageValidCode) throws IOException {
        String code = redisUtil.get(RedisConf.IMG_VALID_ID + RedisConf.SEGMENTATION + tokenHead + validId);
        return code!=null&&imageValidCode.toUpperCase().equals(code.toUpperCase());
    }

    @ApiOperation(value = "退出登录", notes = "退出登录", response = String.class)
    @PostMapping(value = "/logout")
    public String logout() {
        ServletRequestAttributes attribute = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attribute.getRequest();
        String token = request.getAttribute(SysConf.TOKEN).toString();
        if (StringUtils.isEmpty(token)) {
            return ResultUtil.result(SysConf.ERROR, MessageConf.OPERATION_FAIL);
        } else {
            // 获取在线用户信息
            String adminJson = redisUtil.get(RedisConf.MOBILE_LOGIN_TOKEN_KEY + RedisConf.SEGMENTATION + token);
            if (StringUtils.isNotEmpty(adminJson)) {
                OnlineAdmin onlineAdmin = JsonUtils.jsonToPojo(adminJson, OnlineAdmin.class);
                String tokenUid = onlineAdmin.getTokenId();
                // 移除Redis中的TokenUid
                redisUtil.delete(RedisConf.LOGIN_UUID_KEY + RedisConf.SEGMENTATION + tokenUid);
            }
            // 移除Redis中的用户
            redisUtil.delete(RedisConf.MOBILE_LOGIN_TOKEN_KEY + RedisConf.SEGMENTATION + token);
            return ResultUtil.result(SysConf.SUCCESS, MessageConf.OPERATION_SUCCESS);
        }
    }

    @ApiOperation(value = "根据微信code获取token，为管理员登录提供手机入口", notes = "根据微信code获取token，为管理员登录提供手机入口", response = String.class)
    @GetMapping(value = "/wxLoginByCodeForAdmin")
    @Transactional
    public String wxLoginByCodeForAdmin(HttpServletRequest request, @ApiParam(name = "code", value = "code", required = true) @RequestParam(name = "code", required = true) String code) {
        Map result = new HashMap();
        String openId = null;
        String accessToken = null;
        /***
         * 通过微信服务接口获得openId
         */
        String jsonStrOpenId = proxyFeignClient.getOpenId(code);
        try {
            JSONObject jsonOpenId = JSONObject.parseObject(jsonStrOpenId);
            openId = jsonOpenId.getString("openId");
            accessToken = jsonOpenId.getString("accessToken");
            if (openId == null) throw new RuntimeException("openId获取失败");
        } catch (Exception e) {
            e.printStackTrace();
            result.put("code", "9997");
            result.put("msg", jsonStrOpenId);
            return ResultUtil.result(SysConf.SUCCESS, result);
        }
        /***
         * 通过openId登录，查询用户表是否存在，不存在则创建新的用户信息
         */
        UserAdminRVO userAdminRVO = new UserAdminRVO();
        userAdminRVO.setPageSize(1L);
        userAdminRVO.setCurrentPage(1L);
        userAdminRVO.setWeChat(openId);
        Admin admin = null;//通过openId连表查询admin信息
        IPage<UserAdminRVO> pageList = userAdminRService.getPageList(userAdminRVO);
        UserVO userVO = new UserVO();
        UserAdminRVO uar = null;
        if (pageList.getRecords().size() >= 1) {
            uar = pageList.getRecords().get(0);
            if (uar.getAdminUid() == null) {
                result.put("code", "0001");
                result.put("msg", "新用户登录，请先绑定操作员手机号");
                return ResultUtil.result(SysConf.SUCCESS, result);
            } else {
                admin = adminService.getById(uar.getAdminUid());
                /***
                 * 执行登录流程 参考 @PostMapping("/login")
                 */
                List<Role> list = new ArrayList<>();
                Role role = new Role();
                if (admin.getDefaultUid() != null) {
                    QueryWrapper<Role> queryWrapper = new QueryWrapper<>();
                    queryWrapper.eq("owner_uid", admin.getDefaultUid());
                    queryWrapper.eq("status", 1);
                    list = roleService.list(queryWrapper);
                    if (list != null && list.size() > 0) {
                        role = list.get(0);
                    } else {
                        role = null;
                    }
                } else {
                    role = null;
                }
                userVO.setUserName(uar.getUserName());
                userVO.setUid(uar.getUserUid());
                userVO.setNickName(uar.getUserNickName());
                String tokenId = getTokenForUser(userVO, (role == null ? null : role.getOwnerType()),
                        (role == null ? null : role.getOwnerUid()));
                result.put("code", "0000");
                result.put("data", uar);
                result.put("token", tokenId);
                return ResultUtil.result(SysConf.SUCCESS, result);
            }
        } else {
            userVO.setWeChat(openId);
            String jsonStrUserInfo = proxyFeignClient.getUserInfo(openId, accessToken);
            try {
                JSONObject jsonUserInfo = JSONObject.parseObject(jsonStrUserInfo);
                userVO.setNickName(jsonUserInfo.getString("nickName"));
                userVO.setAvatar(jsonUserInfo.getString("headImgurl"));
                userVO.setUserName("用户" + jsonUserInfo.getString("openId").substring(jsonUserInfo.getString("openId").length() - 8, jsonUserInfo.getString("openId").length() - 1));
                userVO.setGender(Integer.valueOf(jsonUserInfo.getString("sex")));
            } catch (Exception e) {
                e.printStackTrace();
                result.put("code", "9997");
                result.put("msg", jsonStrOpenId);
                return ResultUtil.result(SysConf.SUCCESS, result);
            }
            UserVO checkUserVO = userService.getUserWeChat(openId);
            if (checkUserVO == null) {
                userService.add(userVO);
            }
            result.put("code", "0001");
            result.put("msg", "新用户登录，请先绑定操作员手机号");
            return ResultUtil.result(SysConf.SUCCESS, result);
        }
    }

    @ApiOperation(value = "根据微信code获取token，为普通用户登录提供手机入口", notes = "根据微信code获取token，为普通用户登录提供手机入口", response = String.class)
    @GetMapping(value = "/wxLoginByCodeForUser")
    @Transactional
    public String wxLoginByCodeForUser(HttpServletRequest request, @ApiParam(name = "code", value = "code", required = true) @RequestParam(name = "code", required = true) String code) throws Exception {
        Map result = new HashMap();
        String openId = null;
        String accessToken = null;
        /***
         * 通过微信服务接口获得openId
         */
        String jsonStrOpenId = proxyFeignClient.getOpenId(code);
        try {
            JSONObject jsonOpenId = JSONObject.parseObject(jsonStrOpenId);
            openId = jsonOpenId.getString("openId");
            accessToken = jsonOpenId.getString("accessToken");
            if (openId == null) throw new RuntimeException("openId获取失败");
        } catch (Exception e) {
            e.printStackTrace();
            result.put("code", "9997");
            result.put("msg", jsonStrOpenId);
            return ResultUtil.result(SysConf.SUCCESS, result);
        }
        /***
         * 通过openId登录，查询用户表是否存在，不存在则创建新的用户信息
         */
        UserVO userVO = userService.getUserWeChat(openId);
        if (userVO == null) {
            userVO = new UserVO();
            userVO.setWeChat(openId);
            String jsonStrUserInfo = proxyFeignClient.getUserInfo(openId, accessToken);
            try {
                JSONObject jsonUserInfo = JSONObject.parseObject(jsonStrUserInfo);
                userVO.setNickName(jsonUserInfo.getString("nickName"));
                userVO.setAvatar(jsonUserInfo.getString("headImgurl"));
                userVO.setUserName("微信用户" + jsonUserInfo.getString("openId"));
                userVO.setGender(Integer.valueOf(jsonUserInfo.getString("sex")));
                userService.add(userVO);
                UserRoleRVO userRoleRVO = new UserRoleRVO();
                userRoleRVO.setUserUid(userVO.getUid());
                userRoleRService.add(userRoleRVO);
            } catch (Exception e) {
                e.printStackTrace();
                result.put("code", "9997");
                result.put("msg", jsonStrOpenId);
                return ResultUtil.result(SysConf.SUCCESS, result);
            }
            String tokenId = getTokenForUser(userVO, null, null);
            result.put("code", "0001");
            result.put("msg", "新用户登录");
            result.put("data", tokenId);
        } else {
            if (userVO.getStatus() != EStatus.ENABLE) {
                throw new Exception("当前用户不可用请联系管理员");
            }
            /***
             * 判断用户是否长时间未更新数据
             * 由于刷新token是不会触发update事件的，因此此处以该字段为参照是可以正常运作的，除非其他地方有update user的功能。
             */
            Date updateTime = userVO.getUpdateTime();
            long nowMillis = System.currentTimeMillis();
            Date nowDate = new Date(nowMillis);
            // 得到两个日期相差的间隔，秒
            Integer survivalSecond = DateUtils.getSecondByTwoDay(updateTime, nowDate);
            if (survivalSecond > 3600 * 24 * 7) {
                String jsonStrUserInfo = proxyFeignClient.getUserInfo(openId, accessToken);
                try {
                    JSONObject jsonUserInfo = JSONObject.parseObject(jsonStrUserInfo);
                    userVO.setNickName(jsonUserInfo.getString("nickName"));
                    userVO.setAvatar(jsonUserInfo.getString("headImgurl"));
                    userVO.setUserName("微信用户" + jsonUserInfo.getString("openId"));
                    userVO.setGender(Integer.valueOf(jsonUserInfo.getString("sex")));
                    userService.edit(userVO);
                } catch (Exception e) {
                    e.printStackTrace();
                    result.put("code", "9997");
                    result.put("msg", jsonStrOpenId);
                    return ResultUtil.result(SysConf.SUCCESS, result);
                }
            }
            /***
             * userUid查询是否 关注持卡人
             */
            String tokenId = getTokenForUser(userVO, null, null);
            UserEmployeeRVO userEmployeeRVO = new UserEmployeeRVO();
            userEmployeeRVO.setUserUid(userVO.getUid());
            userEmployeeRVO.setPageSize(1L);
            userEmployeeRVO.setCurrentPage(1L);
            IPage<UserEmployeeRVO> iPage = userEmployeeRService.getAllList(userEmployeeRVO);
            if (iPage.getTotal() > 0) {
                UserEmployeeRVO temp = new UserEmployeeRVO();
                temp.setUserUid(userVO.getUid());
                userEmployeeRService.loginGetDefaultFollow(temp);
                result.put("code", "0000");
                result.put("msg", "老用户");
            } else {
                result.put("code", "0001");
                result.put("msg", "老用户（未绑定人员）");
            }
            result.put("data", tokenId);

        }
        //user的登陆记录更新
        result.put("userInfo", userVO);
        userVO.setLoginCount(userVO.getLoginCount() + 1);
        userVO.setLastLoginTime(new Date());
        userVO.setLastLoginIp(IpUtils.getIpAddr(request));//IP获取
        User user = new User();
        BeanUtils.copyProperties(userVO, user);
        user.updateById();
        return ResultUtil.result(SysConf.SUCCESS, result);
    }

    private String getTokenForUser(UserVO userVO, Integer ownerType, String ownerUid) {
        long expiration = audience.getExpiresSecond();
        long nowMillis = System.currentTimeMillis();
        long expMillis = nowMillis + (expiration * 1000);

        String jwtToken;
        jwtToken = jwtTokenUtil.createJWT(
                userVO.getUserName(),
                userVO.getUid(),
                userVO.getNickName(),
                audience.getClientId(),
                audience.getName(),
                ownerType, ownerUid,
                nowMillis,
                expMillis,
                audience.getBase64Secret());
        String token = tokenHead + jwtToken;
        String tokenId = StringUtils.getUUID();
        //存入最后登录时间
        userVO.setLastLoginTime(new Date(nowMillis));
        userService.edit(userVO);
        //将登录数据写到redis中
        redisUtil.setEx(com.zrwl.poscloud.xo.global.RedisConf.MOBILE_LOGIN_TOKEN_KEY
                        + com.zrwl.poscloud.xo.global.RedisConf.SEGMENTATION
                        + token,
                JsonUtils.objectToJson(userVO), expiration, TimeUnit.SECONDS);
        // 在维护一张表，用于 uuid - token 互相转换
        redisUtil.setEx(com.zrwl.poscloud.xo.global.RedisConf.LOGIN_UUID_KEY + com.zrwl.poscloud.xo.global.RedisConf.SEGMENTATION + tokenId,
                token, expiration, TimeUnit.SECONDS);

        return tokenHead + tokenId;
    }
}


